
#include <BOpenGL>
#include <BRender>
#include <BCamera>
#include <BTexture>

#include <BBillboard>
#include "member_BBillboard.h"
#include "member_BTexture.h"

using namespace BWE;

#define member					(*(member_BBillboard*)_ptr)
#define member_allocate()		_ptr = new member_BBillboard(this)
#define member_release()		delete (member_BBillboard*)_ptr

BBillboard::BBillboard()
{
	member_allocate();
}
BBillboard::BBillboard(BReal size)
{
	member_allocate();
	member.size.set(size, size);
}
BBillboard::BBillboard(BReal width, BReal height)
{
	member_allocate();
	member.size.set(width, height);
}
BBillboard::BBillboard(const BBillboard& other)
{
	member_allocate();
	this->copy(other);
}
BBillboard::~BBillboard()
{
	member_release();
}

void BBillboard::copy(const BShape& other)
{
	BShape::copy(other);
}
BShape* BBillboard::clone() const
{
	return new BBillboard(*this);
}

void BBillboard::setSize(BReal width, BReal height)
{
	if (member.size.width() != width || member.size.height() != height)
	{
		member.size.width() = width;
		member.size.height() = height;
		this->dirty();
	}
}
void BBillboard::setSize(const BRealSize& size)
{
	if (member.size != size)
	{
		member.size = size;
		this->dirty();
	}
}
const BRealSize& BBillboard::size()
{
	return member.size;
}

void BBillboard::setFacing(Facing facing)
{
	if (member.facing != facing)
	{
		member.facing = facing;
		this->fresh();
	}
}
BBillboard::Facing BBillboard::facing() const
{
	return member.facing;
}

void BBillboard::setAxis(const BVector& axis)
{
	if (member.axis != axis)
	{
		member.axis = axis;
		if (member.facing == Facing_Axial)
		{
			this->fresh();
		}
	}
}
const BVector& BBillboard::axis() const
{
	return member.axis;
}

const BQuad& BBillboard::quad() const
{
	return member.quad;
}

void BBillboard::refresh(BBox& box)
{
	BReal hw = member.size.width() * BReal(0.5);
	BReal hh = member.size.height() * BReal(0.5);
	member.quad.a() = BVector(-hw, -hh, 0);
	member.quad.b() = BVector(hw, -hh, 0);
	member.quad.c() = BVector(hw, hh, 0);
	member.quad.d() = BVector(-hw, hh, 0);
	BSpace space(member.quad.a());
	space.expand(member.quad.b());
	space.expand(member.quad.c());
	space.expand(member.quad.d());
	BReal half = bMax(space.width(), space.height()) / 2;
	box.reset(half, half, half);
}
void BBillboard::render(BRender& render)
{
	const BCamera* camera = render.camera();
	BVector pos = camera->position();
	BVector up = camera->up();
	BQuater quater;
	if (const BNode* node = render.node())
	{
		BMatrix matrix = node->matrix();
		matrix.inverse();
		pos *= matrix;
		up *= matrix;
		up.normalize();
	}
	BQuad quad = member.quad;
	if (member.facing == Facing_Free)
	{
		BVector vec = pos - this->position();
		vec.normalize();
		quater.set(BVector(0, 0, 1), vec);
		quad.a() *= quater;
		quad.b() *= quater;
		quad.c() *= quater;
		quad.d() *= quater;
		member.facingQuad = quad;
	}

	if (member.facing == Facing_Axial)
	{
		BVector dir = pos - this->position();
		dir.normalize();
		BReal angle = dir.angle(0, 0, 1);
		BQuater quatx(BVector(1, 0, 0), angle);
		BVector vec = BVector(0, 0, 1) * quatx;
		BVector dirxy = BVector(dir.x(), dir.y(), 0).normal();
		BVector vecxy = BVector(vec.x(), vec.y(), 0).normal();
		BReal angle1 = dirxy.angle(vecxy);
		if (dirxy.cross(vecxy).dot(0, 0, 1) < 0)
			angle1 = -angle1;
		quater.set(up, angle1);
		quad.a() = quad.a() * quatx * quater;
		quad.b() = quad.b() * quatx * quater;
		quad.c() = quad.c() * quatx * quater;
		quad.d() = quad.d() * quatx * quater;
		member.facingQuad = quad;
	}

	//glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GEQUAL, 0.5f);
	render.fillQuad(quad);

}
